package db

import (
	"fmt"
	"github.com/golang-jwt/jwt/v5"
	"github.com/gookit/color"
	"net"
	"os"
	"runtime"
	"time"
)

import mqtt "github.com/eclipse/paho.mqtt.golang"

var handle mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
	fmt.Printf("TOPIC: %s", msg.Topic())
	fmt.Printf("MSG: %s", msg.Payload())
}

func InitMqtt(config DataBase) mqtt.Client {
	ss := runtime.GOOS + getHostname() + getIP()
	opt := mqtt.NewClientOptions().AddBroker("tcp://" + config.Host).SetClientID(ss + config.DataBase)
	opt.SetKeepAlive(30 * time.Second)
	opt.SetDefaultPublishHandler(handle)
	opt.SetPingTimeout(1 * time.Second)
	if config.Username != "" && config.Password != "" {
		opt.SetUsername(config.Username)
		opt.SetPassword(config.Password)
	}
	mqttClient := mqtt.NewClient(opt)
	if token := mqttClient.Connect(); token.Wait() && token.Error() != nil {
		color.Redln("mqttClient Init Error")
		panic(token.Error())
	}
	return mqttClient
}

type EmqxClaims[T any] struct {
	*jwt.RegisteredClaims
	UserName string `json:"username"`
	Password string `json:"password"`
	Data     T      `json:"data"`
}

func InitMqttByJwt[T any](config DataBase, jwtSecret []byte, uc EmqxClaims[T]) mqtt.Client {
	tokenStr, _ := EmqxJwt[T](uc, jwtSecret)
	opt := mqtt.NewClientOptions().AddBroker("tcp://" + config.Host).SetClientID(config.DataBase)
	opt.SetKeepAlive(60 * time.Second)
	opt.SetPingTimeout(1 * time.Second)
	opt.SetDefaultPublishHandler(handle)
	opt.SetUsername(tokenStr)
	opt.SetPassword(tokenStr)
	mqttClient := mqtt.NewClient(opt)
	if token := mqttClient.Connect(); token.Wait() && token.Error() != nil {
		color.Redln("mqttClient Init Error")
		panic(token.Error())
	}
	color.Greenln("mqttClient Init Success")
	return mqttClient
}

func EmqxJwt[T any](uc EmqxClaims[T], jwtSecret []byte) (string, error) {
	nowTime := time.Now()
	uc.RegisteredClaims = &jwt.RegisteredClaims{
		ExpiresAt: &jwt.NumericDate{Time: nowTime.Add(5 * time.Hour)},
		Issuer:    "",                              // 签发人
		IssuedAt:  &jwt.NumericDate{Time: nowTime}, //签发时间
	}
	claims := jwt.NewWithClaims(jwt.SigningMethodHS256, uc)
	tokenStr, err := claims.SignedString(jwtSecret)
	if err != nil {
		return "", err
	}
	return tokenStr, nil
}
func ParseEmqxClaims[T any](jwtSecret []byte, tokenString string) (*EmqxClaims[T], error) {
	var t = new(EmqxClaims[T])
	token, err := jwt.ParseWithClaims(tokenString, t, func(token *jwt.Token) (any, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("UnexpectedSigningMethod: %v", token.Header["alg"])
		}
		return jwtSecret, nil
	})
	if err != nil {
		panic(err)
		return nil, err
	}
	if claims, ok := token.Claims.(*EmqxClaims[T]); ok && token.Valid {
		return claims, nil
	}
	return nil, err
}

func getHostname() string {
	hostname, err := os.Hostname()
	if err != nil {
		fmt.Println("get hostname failed, err = ", err.Error())
		return ""
	}
	return hostname
}

func getIP() string {
	conn, error := net.Dial("udp", "8.8.8.8:80")
	if error != nil {
		panic(error)
	}
	defer conn.Close()
	ipAddress := conn.LocalAddr().(*net.UDPAddr)
	return ipAddress.String()
}
